package scotch.compiler.intermediate;
import static me.qmx.jitescript.util.CodegenUtils.p;
import static me.qmx.jitescript.util.CodegenUtils.sig;
import java.util.List;
import java.util.function.Supplier;
import com.google.common.collect.ImmutableList;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import me.qmx.jitescript.CodeBlock;
import me.qmx.jitescript.LambdaBlock;
import scotch.compiler.target.BytecodeGenerator;
import scotch.runtime.Applicable;
import scotch.runtime.Callable;
import scotch.runtime.SuppliedThunk;
@EqualsAndHashCode(callSuper = false)
@ToString
public class IntermediateApply extends IntermediateValue {
private final List<String> captures;
private final IntermediateValue function;
private final IntermediateValue argument;
public IntermediateApply(List<String> captures, IntermediateValue function, IntermediateValue argument) {
this.captures = ImmutableList.copyOf(captures);
this.function = function;
this.argument = argument;
}
@Override
public CodeBlock generateBytecode(BytecodeGenerator generator) {
return new CodeBlock() {{
newobj(p(SuppliedThunk.class));
dup();
captures.forEach(capture -> aload(generator.offsetOf(capture)));
lambda(generator.currentClass(), new LambdaBlock(generator.reserveApply()) {{
function(p(Supplier.class), "get", sig(Object.class));
specialize(sig(Callable.class));
capture(getCaptureTypes());
delegateTo(ACC_STATIC, sig(Callable.class, getCaptureTypes()), new CodeBlock() {{
generator.beginMethod(captures);
append(function.generateBytecode(generator));
invokeinterface(p(Callable.class), "call", sig(Object.class));
checkcast(p(Applicable.class));
append(argument.generateBytecode(generator));
invokeinterface(p(Applicable.class), "apply", sig(Callable.class, Callable.class));
areturn();
generator.endMethod();
}});
}});
invokespecial(p(SuppliedThunk.class), "<init>", sig(void.class, Supplier.class));
}};
}
private Class<?>[] getCaptureTypes() {
int size = captures.size();
Class<?>[] callables = new Class<?>[size];
for (int i = 0; i < size; i++) {
callables[i] = Callable.class;
}
return callables;
}
}